module mycpu_top(
    input aclk,
    input aresetn,
    input [5:0] ext_int,
    
    // ar
    output [3:0] arid,
    output [31:0] araddr,
    output [3:0] arlen,
    output [2:0] arsize,
    output [1:0] arburst,
    output [1:0] arlock,
    output [3:0] arcache,
    output [2:0] arprot,
    output arvalid,
    input arready,
    
    // r
    input [3:0] rid,
    input [31:0] rdata,
    input [1:0] rresp,
    input rlast,
    input rvalid,
    output rready ,
    
    // aw
    output [3:0] awid,
    output [31:0] awaddr,
    output [3:0] awlen,
    output [2:0] awsize,
    output [1:0] awburst,
    output [1:0] awlock,
    output [3:0] awcache,
    output [2:0] awprot,
    output awvalid,
    input awready,
    
    // w
    output [3:0] wid,
    output [31:0] wdata,
    output [3:0] wstrb,
    output wlast,
    output wvalid,
    input wready,
    
    // b
    input [3:0] bid,
    input [1:0] bresp,
    input bvalid,
    output bready,
    
    // debug
    output [31:0] debug_wb_pc,
    output [3 :0] debug_wb_rf_wen,
    output [4 :0] debug_wb_rf_wnum,
    output [31:0] debug_wb_rf_wdata
);
    wire clk, resetn;
    assign clk = aclk;
    assign resetn = aresetn;
    
    // sram interface
    //cpu inst sram
    wire        inst_sram_en;
    wire [3 :0] inst_sram_wen = 4'b0;
    wire [31:0] inst_sram_addr;
    wire [31:0] inst_sram_wdata = 0;
    wire [31:0] inst_sram_rdata;
    //cpu data sram
    wire        data_sram_en;
    wire [3 :0] data_sram_wen;
    wire [31:0] data_sram_addr;
    wire [31:0] data_sram_wdata;
    wire [31:0] data_sram_rdata;
    
    //mips
	wire [31:0] pc;
	wire [31:0] aluout;
    wire stallreg_from_if,stallreg_from_mem;
    mips mips(
        .clk(clk),
        .rst(~resetn),
        
        //IF
        .inst_sram_en(inst_sram_en),
        .pcF(pc),
        .instrF(inst_sram_rdata),
        .stallreg_from_if(stallreg_from_if),
        
        //MEM
        .data_sram_en(data_sram_en),
        .aluoutM(aluout),
        .writedataM(data_sram_wdata),
        .readdataM(data_sram_rdata),
        .datamemEna(data_sram_wen),
        .stallreg_from_mem(stallreg_from_mem),
        
        .debug_wb_pc(debug_wb_pc),
        .debug_wb_rf_wen(debug_wb_rf_wen),
        .debug_wb_rf_wnum(debug_wb_rf_wnum),
        .debug_wb_rf_wdata(debug_wb_rf_wdata),
        .ext_int(ext_int)
    );

    mmu pcdata(
        .inst_vaddr(pc),
        .inst_paddr(inst_sram_addr),
        .data_vaddr({aluout[31:2],2'b00}),
        .data_paddr(data_sram_addr)
    );
    
    instdec instdec(
        .instr(inst_sram_rdata)
    );

    //cache
    wire c_i_ready, c_d_ready;
    assign stallreg_from_if = ~c_i_ready;
    assign stallreg_from_mem = data_sram_en & ~c_d_ready;
    wire m_i_ready, m_d_ready;
    wire m_fetch, m_ld_st;
    wire [31:0] m_i_addr, m_d_addr;
    wire [31:0] mem_st_data, mem_data;
    wire m_d_rw;
    wire [3:0] m_d_sel;
    
    i_cache i_cache_t(
        .p_a(inst_sram_addr),
        .p_din(inst_sram_rdata),
        .p_strobe(inst_sram_en),
        .p_ready(c_i_ready),
        .clk(clk),
        .clrn(resetn),
        .m_a(m_i_addr),
        .m_dout(mem_data),
        .m_strobe(m_fetch),
        .m_ready(m_i_ready)
    );
    d_cache d_cache_t(
        .p_a(data_sram_addr),
        .p_dout(data_sram_wdata),
        .p_din(data_sram_rdata),
        .p_strobe(data_sram_en),
        .data_sram_wen(data_sram_wen),
        .p_ready(c_d_ready),
        .clk(clk),
        .clrn(resetn),
        .m_a(m_d_addr),
        .m_dout(mem_data),
        .m_din(mem_st_data),
        .m_strobe(m_ld_st),
        .m_rw(m_d_rw),
        .m_sel(m_d_sel),
        .m_ready(m_d_ready)
    );

    //axi interface
    wire mem_ready;
    reg [1:0] state;
    localparam IDLE = 2'b00;
    localparam FETCH = 2'b01;
    localparam LD_ST = 2'b10;
    always @(posedge clk) begin
        if (~resetn) begin
            state <= IDLE; 
        end else begin
            case (state)
                IDLE: begin
                    state <= m_fetch ? FETCH:
                            m_ld_st ? LD_ST:
                            IDLE;
                end
                FETCH: begin
                    state <= mem_ready & m_ld_st ? LD_ST:
                            mem_ready & ~m_ld_st ? IDLE:
                            FETCH;
                end
                LD_ST: begin
                    state <= mem_ready? IDLE:
                            LD_ST;
                end
            endcase
        end
    end
    assign m_i_ready = (state == FETCH) & mem_ready;
    assign m_d_ready = (state == LD_ST) & mem_ready;
    wire sel_i =  ~resetn
                | (state == IDLE) & m_fetch
                | (state == FETCH) & ~mem_ready;
    wire [31:0]  m_addr = sel_i ? m_i_addr : m_d_addr;
    wire mem_access = sel_i ? m_fetch : m_ld_st;
    wire mem_write = sel_i ? 1'b0 : m_d_rw;
    wire [1:0] mem_size = 2'b10;
    wire [3:0] m_sel = sel_i ? 4'b0000 : m_d_sel;
    
    axi_interface cpu_axi_interface(
        .clk(clk),
        .resetn(resetn),
        
        .mem_a(m_addr),
        .mem_access(mem_access),
        .mem_write(mem_write),
        .mem_size(mem_size),
        .mem_sel(m_sel),
        .mem_ready(mem_ready),
        .mem_st_data(mem_st_data),
        .mem_data(mem_data),
        
        .arid(arid),
        .araddr(araddr),
        .arlen(arlen),
        .arsize(arsize),
        .arburst(arburst),
        .arlock(arlock),
        .arcache(arcache),
        .arprot(arprot),
        .arvalid(arvalid),
        .arready(arready),
        
        .rid(rid),
        .rdata(rdata),
        .rresp(rresp),
        .rlast(rlast),
        .rvalid(rvalid),
        .rready(rready),
        
        .awid(awid),
        .awaddr(awaddr),
        .awlen(awlen),
        .awsize(awsize),
        .awburst(awburst),
        .awlock(awlock),
        .awcache(awcache),
        .awprot(awprot),
        .awvalid(awvalid),
        .awready(awready),
        
        .wid(wid),
        .wdata(wdata),
        .wstrb(wstrb),
        .wlast(wlast),
        .wvalid(wvalid),
        .wready(wready),
        
        .bid(bid),
        .bresp(bresp),
        .bvalid(bvalid),
        .bready(bready)
    );
endmodule